<?php
/**
 * 打印变量,断点
 * @param unknown $a 变量
 * @param string $b 是否打印变量类型
 */
function t($a, $b = false)
{
    echo "<pre>";
    if ($b) {
        var_dump($a);
    } else {
        print_r($a);
    }
    exit("</pre>");
}


/**
 * 页面输出
 *
 * @param string $msg  信息
 * @param string $exit 是否阻断
 */
function e($msg = '', $exit = false)
{
    if ($exit) {
        exit("------------------{$msg}-------------------<br>");
    } else {
        echo ("------------------{$msg}-------------------<br>");
    }
}
class Db{
    public $con; #链接标识
    public $debug = false;
    public $logData = [];
    //初始化链接
    public function __construct($config=[]){
        if(!$config){
          #域名
          $host = explode(":",$_SERVER['HTTP_HOST']);
          $host = $host[0];
          #主域名
          $host = explode('.',$host);
          $len  = count($host);
          $main_domain = strtolower($host[$len-2].'.'.$host[$len-1]);

          #域名里的配置
          $rs = include 'Env.php';
          foreach($rs['domain'] as $k => $v){
            if($k==$main_domain){
              $config = $v['db'];
              break;
            }
          }
        }
        $this->mysqli= new mysqli($config['host'],$config['user'],$config['pwd'],$config['dbname']);
        if ($this->mysqli->connect_errno){
            die('Connect Error: ' . $this->mysqli->connect_errno);
        }else{
            $this->mysqli->query('set names utf8');
            $this->mysqli->autocommit(true);
            $this->debug = false;
        }
        return $this;
    }

    //开始事务
    public function begin(){
      $this->mysqli->autocommit(false);
      $this->mysqli->begin_transaction();
    }

    //执行事务
    public function end($arr){
        $check = true;
        foreach($arr as $v){
            if(!$v){
                $check = false;
                break;
            }
        }
        if($check){
            $this->mysqli->commit();
        }else{
          $this->mysqli->rollback();
        }
        $this->mysqli->autocommit(true);
        return $check;
    }

    //缓存sql
    public function cache($sql,$vars=[],$limit=''){
        return $this->query($sql,$vars,$limit,false,true);
    }

    /**
     * 执行sql语句
     * @param  [type]  $sql     [sql语句]
     * @param  array   $vars    [参数]
     * @param  string  $limit   [取多前少条]
     * @param  boolean $isCount [是否统计总条数, 用于分页]
     * @param  boolean $isCache [是否缓存,  todo]
     * @return [type]           [按sql类型返回数据]
     */
    public function query($sql,$vars=[],$limit='', $isCount=false,$isCache=false){
      $limit = (int)$limit;
      $sql   = trim($sql);
      $temp_sql = $this->logSql($sql, $vars);

      #分情况
      $arr  = explode(" ",$sql);
      $type = trim(strtolower($arr[0]));
      switch ($type) {
          case 'select':
            if($isCount){
              $sql = "select SQL_CALC_FOUND_ROWS ".ltrim($sql,'select');
            }else{
              if($isCache){
                  //todo: 缓存, 暂时不做
              }
            }
            $stmt = $this->mysqli->prepare($sql);
            if(!$stmt && $this->debug){
                e("预编译sql错误:".$temp_sql);
            }
            if($vars) call_user_func_array([$stmt, 'bind_param'], $this->_bind_param($vars));
            $stmt->execute();
            if($stmt->error && $this->debug){
              e($temp_sql.' : '.$stmt->error);
            }
            $rs  = [];
            $row   = [];
            $params = [];
            #找出所有字段名称
            $meta = $stmt->result_metadata();
            while($field = $meta->fetch_field()){
                $params[] = &$row[$field->name];
            }
            #绑定所有字段
            call_user_func_array(array($stmt, 'bind_result'), $params);
            $i=0;
            while($stmt->fetch())
            {
                $rs[$i] = array();
                foreach($row as $k=>$v)
                    $rs[$i][$k] = $v;
                $i++;
            }
            //取前多少条
            if($limit===1 && isset($rs[0])){
              $rs = $rs[0];
            }else if($limit){
              $limit = $limit<count($rs)?$limit:count($rs);
              $temp = [];
              for($i=0; $i<$limit; $i++){
                $temp[] = $rs[$i];
              }
              $rs = $temp;
            }else{
              //获取条数, 用于分页
              if($isCount){
                $stmt       = $this->mysqli->query('SELECT FOUND_ROWS()');
                $totalCount = $stmt->fetch_row();
                $rs = [
                    $rs,
                    isset($totalCount[0])?$totalCount[0]:0,
                ];
              }
            }
            $stmt->close();
            break;
          case 'update':
              $stmt = $this->mysqli->prepare($sql);
              if(!$stmt && $this->debug){
                  e("预编译sql错误:".$temp_sql);
              }
              if($vars) call_user_func_array([$stmt, 'bind_param'], $this->_bind_param($vars));
              $stmt->execute();
              if($stmt->error && $this->debug){
                e($temp_sql.' : '.$stmt->error);
              }
              $rs = $stmt->affected_rows;
              $stmt->close();
              break;
          case 'delete':
              $stmt = $this->mysqli->prepare($sql);
              if(!$stmt && $this->debug){
                  e("预编译sql错误:".$temp_sql);
              }
              if($vars) call_user_func_array([$stmt, 'bind_param'], $this->_bind_param($vars));
              $stmt->execute();
              if($stmt->error && $this->debug){
                e($temp_sql.' : '.$stmt->error);
              }
              $rs = $stmt->affected_rows;
              $stmt->close();
              break;
          case 'insert':
              $stmt = $this->mysqli->prepare($sql);
              if(!$stmt && $this->debug){
                  e("预编译sql错误:".$temp_sql);
              }
              if($vars) call_user_func_array([$stmt, 'bind_param'], $this->_bind_param($vars));
              $stmt->execute();
              if($stmt->error && $this->debug){
                e($temp_sql.' : '.$stmt->error);
              }
              $rs = $stmt->insert_id;
              $stmt->close();
              break;
          default:
              e('sql 错误');
              break;
      }
      return $rs;
    }
    /**
     * 格式化参数绑定
     * @param  [type] $vars [description]
     * @return [type]       [description]
     */
    private function _bind_param($vars){
      $temp = [''];
      foreach($vars as $v){
        $type = '';
        switch (gettype($v)) {
            case 'NULL':
            case 'string':
                $type = 's';
                break;
            case 'boolean':
            case 'integer':
                $type = 'i';
                break;
            case 'blob':
                $type = 'b';
                break;
            case 'double':
                $type = 'd';
                break;
        }
        $temp[0] .= $type;
        array_push($temp,$v);
      }
      $refs = [];
      foreach ($temp as $key => $value) {
          $refs[$key] = & $temp[$key];
      }
      return $refs;
    }
    //查找单条数据
    public function find($table,$id='', $fields='*'){
        $sql = "select {$fields} from `{$table}` WHERE id = ?";
        $vars= [(int)$id];
        return $this->query($sql,$vars,1);
    }
    /**
     * 查找数据,二维
     * @param  [type] $table  [表名称]
     * @param  string $where  [查找条件]
     * $where 格式:  ['sex=? and name=?', [1, '张三']]
     * @param  string $fields [查找字段]
     * @return [type]         [description]
     */
    public function select($table,$where=[], $fields='*'){
        $sql = "select {$fields} from `{$table}` ";
        if($where){
            $sql .= ' where '.$where[0];
        }
        return $this->query($sql,$where[1]);
    }
    /**
     * 更新表,
     * @param  [type] $table [表名称]
     * @param  [type] $data  [更新数组,键值对]
     * @param  array  $where [更新条件]
     * $where 格式:  ['sex=? and name=?', [1, '张三']]
     * @return [type]        [description]
     */
    public function update($table, $data, $where=[]){
        $temp = [];
        $vars = [];
        foreach ($data as $k => $v){
            if($k == 'id') continue;
            $vars[] = $v;
            array_push($temp, "`$k`=?");
        }
        if(!$temp) e('缺少更新内容');
        $sql = "update `{$table}` set ".implode(',', $temp);
        #id作为更新条件
        if(isset($data['id']) && !$where){
            $sql .= " where id=".(int)$data['id'];
        }else{
            if($where){
                $sql .= ' where '.$where[0];
                $vars = array_merge($vars,$where[1]);
            }else{
                e('update快捷方式不能缺少条件');
            }
        }
        return $this->query($sql,$vars);
    }
    //插入
    public function insert($table, $data){
        $keys = [];
        $vars = [];
        foreach($data as $k => $v){
            $keys[] = $k;
            $vars[] = $v;
        }
        $sql = "insert into `{$table}` (`".implode('`,`', $keys)."`) values (".countZwf($vars).")";
        return $this->query($sql,$vars);
    }
    //插入多条
    public function insertAll($table, $data){
        $db_val = '';
        $vars = [];
        foreach($data as $k => $v){
            $keys = [];
            $db_val .= "(";
            foreach($v as $key => $val){
                $keys[] = "`{$key}`";
                $vars[] = $val;
                $db_val .= "?,";
            }
            $db_val = rtrim($db_val,",");
            $db_val .= "),";
        }
        $db_val = rtrim($db_val,",");
        $sql = "insert into `{$table}` (".implode(',',$keys).") values ".$db_val;
        return $this->query($sql,$vars);
    }
    //删除
    public function delete($table,$val='' ,$fields='id'){
        $sql = "delete from `{$table}` WHERE `$fields`=?";
        $vars = [$val];
        return $this->query($sql,$vars);
    }

    //组装占位符
    public function logSql($sql,$vars){
        $t = md5(time());
        $sql = str_replace("?",$t,$sql);
        foreach($vars as $v){
            if(is_string($v)){
              $sql = preg_replace("/$t/","'{$v}'",$sql,1);
            }else{
              $sql = preg_replace("/$t/","{$v}",$sql,1);
            }
        }
        $this->logData[] = $sql;
        return $sql;
    }
    //显示sql
    public function getSql(){
        t($this->logData);
    }
    public function close(){
      @$this->mysqli->close();
    }
    public function __destruct(){
        $this->close();
    }
    public function __clone(){
        e('Clone is not allow!');
    }
}
